package com.tarena.lbs.business.servcie.impl;

import com.alibaba.nacos.common.utils.CollectionUtils;
import com.github.pagehelper.PageInfo;
import com.tarena.lbs.attach.api.AttachApi;
import com.tarena.lbs.attach.pojo.dto.AttachDTO;
import com.tarena.lbs.attach.pojo.param.AttachQRParam;
import com.tarena.lbs.attach.pojo.param.AttachUpdateParam;
import com.tarena.lbs.attach.pojo.param.PicUpdateParam;
import com.tarena.lbs.attach.pojo.query.AttachQuery;
import com.tarena.lbs.base.common.utils.Asserts;
import com.tarena.lbs.base.protocol.exception.BusinessException;
import com.tarena.lbs.base.protocol.pager.PageResult;
import com.tarena.lbs.business.constant.BusinessTypes;
import com.tarena.lbs.business.dao.repository.StoreESRepository;
import com.tarena.lbs.business.dao.repository.StoreRepository;
import com.tarena.lbs.business.pojo.param.StoreParam;
import com.tarena.lbs.business.pojo.param.UserLocationParam;
import com.tarena.lbs.business.pojo.po.BusinessPO;
import com.tarena.lbs.business.pojo.po.StorePO;
import com.tarena.lbs.business.pojo.po.StoreSearchPO;
import com.tarena.lbs.business.pojo.query.AreaStoreQuery;
import com.tarena.lbs.business.pojo.query.StoreQuery;
import com.tarena.lbs.business.pojo.vo.NearStoreVO;
import com.tarena.lbs.business.pojo.vo.PictureVO;
import com.tarena.lbs.business.pojo.vo.StoreVO;
import com.tarena.lbs.business.producer.AmqpProducer;
import com.tarena.lbs.business.servcie.StoreService;
import com.tarena.lbs.common.constant.AmqpDeclareConst;
import com.tarena.lbs.common.constant.CacheKeyConst;
import com.tarena.lbs.common.enums.BusinessResultEnum;
import com.tarena.lbs.common.enums.Roles;
import com.tarena.lbs.common.event.LocationEvent;
import com.tarena.lbs.common.principle.UserPrinciple;
import com.tarena.lbs.common.utils.AuthenticationContextUtils;
import com.tarena.lbs.user.api.UserApi;
import com.tarena.lbs.user.api.pojo.dto.AdminDto;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.GeoOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Service
@Slf4j
public class StoreServiceImpl implements StoreService {

    @Autowired
    private StoreRepository storeRepository;

    @DubboReference
    private AttachApi attachApi;

    @DubboReference
    private UserApi userApi;

    @Autowired
    private AmqpProducer<LocationEvent> amqpProducer;

//    @Autowired
//    private StoreESRepository storeESRepository;

    @Autowired
    private GeoOperations geoOps;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void save(StoreParam param) throws BusinessException {
        //1.检查当前登录用户
        UserPrinciple userPrinciple = AuthenticationContextUtils.get();
        if (userPrinciple == null) {
            throw new BusinessException(BusinessResultEnum.USER_NOT_LOGIN);
        }
        //2.检查当前登录用户的角色是否符合我当前业务的角色要求
        //ADMIN 平台管理员 才能创建商家  SHOP 商家账号 能创建店铺
        checkRole(Roles.SHOP);
        //3.检查商家是否存在
        BusinessPO businessPO = storeRepository.getBusinessById(param.getBusinessId());
        if (businessPO == null) {
            throw new BusinessException(BusinessResultEnum.BUSINESS_NOT_EXISTS);
        }

        if (param.getId() != null) {
            //更新逻辑
            StorePO storePO = storeRepository.getStoreById(param.getId());
            if (storePO == null) {
                throw new BusinessException(BusinessResultEnum.STORE_NOT_EXISTS);
            }
            param.setStoreLogo(storePO.getStoreLogo());
            updateStore(param);
            //判断位置是否一致
            if (!storePO.getStoreLongitude().equals(param.getLongitude()) || !storePO.getStoreLatitude().equals(param.getLatitude())){
                String key = CacheKeyConst.GEO_STORE_LOCATION_KEY;
                //重新保存Redis
                RedisGeoCommands.GeoLocation lc
                        = new RedisGeoCommands.GeoLocation(param.getId(),
                        new Point(Double.parseDouble(param.getLongitude()), Double.parseDouble(param.getLatitude())));
                geoOps.add(key, lc);
            }
            //7.新增之后,有了id,调用图片绑定
            bindPictures(param.getId(), param);

        } else {
            //新增逻辑
            //4.幂等验证正常 可以新增 封装 数据对象PO 执行save新增
            Integer id = insertStore(param);
            //5.保存数据到ES--改成用redis
            /*
                StoreSearchPO storeSearchPO = new StoreSearchPO();
                storeSearchPO.setId(id);
                storeSearchPO.setLocation(param.getLongitude()+","+param.getLatitude());
                storeSearchPO.setStoreName(param.getStoreName());
                storeSearchPO.setBusinessId(param.getBusinessId());
                storeESRepository.save(storeSearchPO);
            */

            //6.保存到Redis中,方便位置计算
            String key = CacheKeyConst.GEO_STORE_LOCATION_KEY;
            RedisGeoCommands.GeoLocation lc
                    = new RedisGeoCommands.GeoLocation(id,
                    new Point(Double.parseDouble(param.getLongitude()), Double.parseDouble(param.getLatitude())));
            geoOps.add(key, lc);

            //7.新增之后,有了id,调用图片绑定
            bindPictures(id, param);
        }

    }

    private Integer insertStore(StoreParam param) {
        StorePO storePO = new StorePO();
        BeanUtils.copyProperties(param, storePO);
        storePO.setStoreLatitude(param.getLatitude());
        storePO.setStoreLongitude(param.getLongitude());
        storePO.setStoreImagesId(param.getStoreImagesIds().toString());
        storePO.setCreateTime(new Date());
        storePO.setUpdateTime(storePO.getCreateTime());
        storeRepository.insertStore(storePO);
        return storePO.getId();
    }

    private void updateStore(StoreParam param) {
        StorePO storePO = new StorePO();
        BeanUtils.copyProperties(param, storePO);
        storePO.setStoreLatitude(param.getLatitude());
        storePO.setStoreLongitude(param.getLongitude());
        storePO.setStoreImagesId(param.getStoreImagesIds().toString());
        storePO.setUpdateTime(new Date());
        storeRepository.updateStore(storePO);
    }


    @Override
    public PageResult<StoreVO> pageList(StoreQuery query) throws BusinessException {
        log.debug("Store 分页查询参数:{}", query);
        //1.检查当前登录用户
        UserPrinciple userPrinciple = AuthenticationContextUtils.get();
        if (userPrinciple == null) {
            throw new BusinessException(BusinessResultEnum.USER_NOT_LOGIN);
        }
        //2.检查当前登录用户的角色是否符合我当前业务的角色要求
        //ADMIN 平台管理员 才能创建商家  SHOP 商家账号 能创建店铺
        checkRole(Roles.SHOP);
        AdminDto adminDto = userApi.getAdminById(userPrinciple.getId());
        if (adminDto == null) {
            throw new BusinessException(BusinessResultEnum.BUSINESS_NOT_EXISTS);
        }
        //3.查询条件增加商家id
        query.setBusinessId(adminDto.getBusinessId());

        PageResult<StoreVO> voPage = new PageResult<>();
        PageInfo<StorePO> pageInfo = storeRepository.pagesList(query);
        //pageNo pageSize total
        // 将分页信息中的页码设置到分页结果对象中
        voPage.setPageNo(pageInfo.getPageNum());
        // 将分页信息中的每页数量设置到分页结果对象中
        voPage.setPageSize(pageInfo.getPageSize());
        // 将分页信息中的总记录数设置到分页结果对象中
        voPage.setTotal(pageInfo.getTotal());

        // 从分页信息中获取管理员持久化对象列表
        List<StorePO> pos = pageInfo.getList();
        // 声明一个存储管理员视图对象的列表
        List<StoreVO> vos = assemblePOToV0(pos);
        // 判断持久化对象列表是否不为空
        voPage.setObjects(vos);
        return voPage;
    }

    @Override
    public PageResult<StoreVO> getAreaStorePageList(AreaStoreQuery query) throws BusinessException {
        log.debug("getAreaStorePageList 分页查询参数:{}", query);
        //1.检查当前登录用户
        UserPrinciple userPrinciple = AuthenticationContextUtils.get();
        if (userPrinciple == null) {
            throw new BusinessException(BusinessResultEnum.USER_NOT_LOGIN);
        }
        //2.获取business ID
        AdminDto adminDto = userApi.getAdminById(userPrinciple.getId());
        if (adminDto == null) {
            throw new BusinessException(BusinessResultEnum.BUSINESS_NOT_EXISTS);
        }
        //3.查询条件增加商家id
        Integer businessId = adminDto.getBusinessId();

        PageResult<StoreVO> voPage = new PageResult<>();
        PageInfo<StorePO> pageInfo = storeRepository.getAreaStorePageList(businessId, query);

        //pageNo pageSize total
        // 将分页信息中的页码设置到分页结果对象中
        voPage.setPageNo(pageInfo.getPageNum());
        // 将分页信息中的每页数量设置到分页结果对象中
        voPage.setPageSize(pageInfo.getPageSize());
        // 将分页信息中的总记录数设置到分页结果对象中
        voPage.setTotal(pageInfo.getTotal());
        // 从分页信息中获取管理员持久化对象列表
        List<StorePO> pos = pageInfo.getList();
        // 声明一个存储管理员视图对象的列表
        List<StoreVO> vos = assemblePOToV0(pos);
        voPage.setObjects(vos);
        return voPage;
    }

    @Override
    public StoreVO getStoreById(Integer storeId) {
        StoreVO vo = new StoreVO();
        StorePO po = storeRepository.getStoreById(storeId);
        BeanUtils.copyProperties(po, vo);
        String imagesIdStr = po.getStoreImagesId().replace("[", "").replace("]", "");
        String[] imagesIds = imagesIdStr.split(",");
        AttachQuery param = new AttachQuery();
        param.setBusinessId(storeId);
        param.setBusinessType(300);
        List<AttachDTO> pics = attachApi.getAttachInfoByParam(param);
        param.setBusinessType(400);
        List<AttachDTO> logo = attachApi.getAttachInfoByParam(param);

        List<PictureVO> pictureVOS = new ArrayList<>();
        for (String imagesId : imagesIds) {
            String id = imagesId.trim();
            PictureVO pictureVO = new PictureVO();
            pictureVO.setId(Integer.parseInt(id));
            for (AttachDTO pic : pics) {
                if (pic.getId().equals(Integer.parseInt(id))) {
                    pictureVO.setImage("http://localhost:9081/static/" + pic.getFileUuid());
                }
            }
            pictureVOS.add(pictureVO);
        }
        vo.setPictureVOS(pictureVOS);
        vo.setStoreLogo(po.getStoreLogo());
        if (logo != null && !logo.isEmpty()) {
            String logoUrl = "http://localhost:9081/static/" + logo.get(0).getFileUuid();
            vo.setStoreLogo(logoUrl);
            vo.setStoreLogoPic(logoUrl);
        }
        return vo;
    }

    @Override
    public void deleteStore(Integer storeId) throws BusinessException {
        UserPrinciple userPrinciple = AuthenticationContextUtils.get();
        if (userPrinciple == null) {
            throw new BusinessException(BusinessResultEnum.USER_NOT_LOGIN);
        }
        AdminDto adminDto = userApi.getAdminById(userPrinciple.getId());

        //TODO 删除图片文件
        //List<AttachUpdateParam> params = new ArrayList<>();
        //StorePO po = storeRepository.getStoreById(storeId);
        //attachApi.batchUpdateAttachByIdList(params);
        storeRepository.deleteStore(storeId, adminDto.getBusinessId());
    }

    @Override
    public List<NearStoreVO> getNearStores(UserLocationParam param) throws BusinessException {
        List<NearStoreVO> geoPos = storeRepository.getNearStores(param);
        if (geoPos == null || geoPos.isEmpty()) {
            log.debug("没有命中任何店铺数据！");
        }
        //排序按距离升序
        return geoPos;
    }

    @Override
    public void postLocation(UserLocationParam param) throws BusinessException {
        UserPrinciple userPrinciple = AuthenticationContextUtils.get();
        if (userPrinciple == null) {
            throw new BusinessException(BusinessResultEnum.USER_NOT_LOGIN);
        }
        param.setUserId(userPrinciple.getId());
        List<NearStoreVO> stores = getNearStores(param);
        //循环店铺 发送 优惠券消息（用户id，店铺id） 表示 用户可以从这个店铺领取优惠券
        if (CollectionUtils.isNotEmpty(stores)) {
            stores.forEach(store -> {
                //组织消息
                LocationEvent locationEvent = new LocationEvent();
                locationEvent.setUserId(store.getUserId());
                locationEvent.setStoreId(store.getStoreId());
                //将消息发送
                amqpProducer.send(locationEvent, AmqpDeclareConst.NEAR_STORES_EX, AmqpDeclareConst.NEAR_STORES_RK);
            });
        }
    }

    @Override
    public void startOnceWarmup() {
        Boolean exits = storeRepository.geoStationsExists();
        if (!exits) {
            log.info("不存在缓存，开始所有场站缓存预热！");
            Long total = storeRepository.countAll();
            int pageSize = 10000;
            int pageNum = (int) ((total + pageSize - 1) / pageSize); // 计算总页数

            for (int i = 1; i <= pageNum; i++) {
                log.info("开始预热第{}批次！", i);
                // 使用 PageHelper 进行分页查询
                List<StorePO> pos = storeRepository.getStationsPage(i, pageSize);
                log.info("本次预热数据为 {}", pos);
                storeRepository.saveGeos(pos);
            }
        }
    }

    @Override
    public void disableStore(Integer storeId) throws BusinessException {
        UserPrinciple userPrinciple = AuthenticationContextUtils.get();
        if (userPrinciple == null) {
            throw new BusinessException(BusinessResultEnum.USER_NOT_LOGIN);
        }
        AdminDto adminDto = userApi.getAdminById(userPrinciple.getId());

        storeRepository.disableStore(storeId, adminDto.getBusinessId());
    }

    @Override
    public void enableStore(Integer storeId) throws BusinessException {
        UserPrinciple userPrinciple = AuthenticationContextUtils.get();
        if (userPrinciple == null) {
            throw new BusinessException(BusinessResultEnum.USER_NOT_LOGIN);
        }
        AdminDto adminDto = userApi.getAdminById(userPrinciple.getId());

        storeRepository.enableStore(storeId, adminDto.getBusinessId());
    }

    private void checkRole(Roles roles) throws BusinessException {
        UserPrinciple userPrinciple = AuthenticationContextUtils.get();
        if (!userPrinciple.getRole().equals(roles)) {
            throw new BusinessException(BusinessResultEnum.USER_ROLE_NOT_MATCH);
        }
    }

    private String getFileUuidFromUrl(String licenseUrl) {
        return licenseUrl.substring(licenseUrl.lastIndexOf("/") + 1);
    }

    private void bindPictures(Integer storeId, StoreParam param) throws BusinessException {
        //店铺新增 图片 要绑定多张 一个是店铺照片多个 一张logo
        //准备一下 接口的入参
        List<PicUpdateParam> picParams = new ArrayList<>();

        List<String> imagesIds = param.getStoreImagesIds();
        for (String imagesId : imagesIds) {
            log.info("图片id:{}", imagesId);
            //准备一个PicUpdateParam 存储商店实体图片 的绑定参数
            PicUpdateParam licenseParam = new PicUpdateParam();
            //bizType 100 表示商家营业执照
            licenseParam.setBusinessType(BusinessTypes.BIZ_STORE_IMAGES);
            //bizId 商家id
            licenseParam.setBusinessId(storeId);
            //可以在对象里绑定图片id或者图片fileUuid 当前业务无法获取图片id只能获取fileUuid
            //http://localhost:9081/static/19fh93gr931h10rf3h29r.png 最后的值是fileUuid的拼接 19fh93gr931h10rf3h29r.png
            licenseParam.setId(Integer.valueOf(imagesId));
            picParams.add(licenseParam);
        }

        //准备第二个绑定图片的入参元素 绑定logo
        if (param.getStoreLogo() != null) {
            PicUpdateParam logoParam = new PicUpdateParam();
            logoParam.setBusinessType(BusinessTypes.BIZ_STORE_LOGO);
            logoParam.setBusinessId(storeId);
            String logoId = param.getStoreLogo();
            logoParam.setId(Integer.valueOf(logoId));
            //将营业执照图片数据 和log图片数据放到list中
            picParams.add(logoParam);
        }
        if (picParams.isEmpty()) {
            return;
        }
        boolean result = attachApi.batchUpdateBusiness(picParams);
        Asserts.isTrue(!result, new BusinessException("-2", "业务绑定失败"));
    }

    private List<StoreVO> assemblePOToV0(List<StorePO> pos) {
        List<StoreVO> vos = new ArrayList<>();
        // 判断持久化对象列表是否不为空
        if (CollectionUtils.isNotEmpty(pos)) {
            vos = pos.stream().map(po -> {
                StoreVO vo = new StoreVO();
                BeanUtils.copyProperties(po, vo);
                String imagesIdStr = po.getStoreImagesId().replace("[", "").replace("]", "");
                String[] imagesIds = imagesIdStr.split(",");
                List<PictureVO> pictureVOS = new ArrayList<>();
                for (String imagesId : imagesIds) {
                    PictureVO pictureVO = new PictureVO();
                    pictureVO.setId(Integer.parseInt(imagesId.trim()));
                    pictureVOS.add(pictureVO);
                }
                vo.setPictureVOS(pictureVOS);
                vo.setStoreLogoPic(po.getStoreLogo());
                return vo;
            }).collect(Collectors.toList());
        }
        return vos;
    }

}
